/*
 *    Copyright (c) 2018-2025, lengleng All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the pig4cloud.com developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: lengleng (wangiegie@gmail.com)
 */

package com.pig4cloud.pig.ads.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.dy.yunying.api.constant.DistributedLock;
import com.google.api.client.util.Lists;
import com.pig4cloud.pig.ads.config.SdkProperties;
import com.pig4cloud.pig.ads.service.AdMaterialPlatformService;
import com.pig4cloud.pig.ads.service.AdMaterialService;
import com.pig4cloud.pig.api.entity.AdMaterial;
import com.pig4cloud.pig.api.entity.AdMaterialPlatform;
import com.pig4cloud.pig.api.entity.AdMaterialReportDO;
import com.pig4cloud.pig.api.util.Constants;
import com.pig4cloud.pig.api.vo.AdMaterialPageVo;
import com.pig4cloud.pig.api.vo.AdMaterialReportVo;
import com.pig4cloud.pig.api.vo.AdMaterialVo;
import com.pig4cloud.pig.api.vo.UserByMaterialVo;
import com.pig4cloud.pig.common.core.util.R;
import com.pig4cloud.pig.common.log.annotation.SysLog;
import com.pig4cloud.pig.common.security.annotation.Inner;
import com.pig4cloud.pig.common.security.util.SecurityUtils;
import io.swagger.annotations.Api;
import lombok.AllArgsConstructor;
import lombok.Cleanup;
import lombok.SneakyThrows;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;


/**
 * 素材库
 *
 * @author pigx code generator
 * @date 2021-06-23 14:32:25
 */
@RestController
@AllArgsConstructor
@RequestMapping("/material")
@Api(value = "material", tags = "素材库管理")
public class AdMaterialController {

	private final AdMaterialService adMaterialService;

	private final AdMaterialPlatformService adMaterialPlatformService;
	private final SdkProperties sdkProperties;

	/**
	 * 人物列表
	 *
	 * @param req
	 * @return
	 */
	@SysLog("人物列表")
	@RequestMapping("/getUserByMaterial")
	public R getUserByMaterial(UserByMaterialVo req) {
		return adMaterialService.getUserByMaterial(req);
	}




	/**
	 * 视频素材 - 页面
	 *
	 * @param req
	 * @return
	 */
	@SysLog("视频素材列表")
	@RequestMapping("/getVideoPage")
	public R getVideoPage(@RequestBody AdMaterialPageVo req) {
		req.setType("1");
		req.setIsDelete("0");
		req.setReadpop("0");
		req.setStatus("0");
		return adMaterialService.getPage(req);
	}

	/**
	 * 图片素材 - 页面
	 *
	 * @param req
	 * @return
	 */
	@SysLog("图片素材列表")
	@RequestMapping("/getPicturePage")
	public R getPicturePage(@RequestBody AdMaterialPageVo req) {
		req.setType("2");
		req.setIsDelete("0");
		req.setReadpop("0");
		req.setStatus("0");
		return adMaterialService.getPage(req);
	}

	/**
	 * 视频素材 - 前端分页
	 *
	 * @param req
	 * @return
	 */
	@SysLog("视频素材列表")
	@RequestMapping("/getVideoList")
	public R getVideoList(@RequestBody AdMaterialVo req) {
		req.setType("1");
		req.setIsDelete("0");
		req.setReadpop("0");
		req.setStatus("0");
		return adMaterialService.getList(req);
	}

	/**
	 * 素材 - 下拉框
	 * @param req
	 * @return
	 */
	@SysLog("素材下拉框")
	@RequestMapping("/getDownList")
	public R getDownList(@RequestBody AdMaterialVo req) {
		return adMaterialService.getDownList(req);
	}

	/**
	 * 图片素材 - 前端分页
	 *
	 * @param req
	 * @return
	 */
	@SysLog("图片素材列表")
	@RequestMapping("/getPictureList")
	public R getPictureList(@RequestBody AdMaterialVo req) {
		req.setType("2");
		req.setIsDelete("0");
		req.setReadpop("0");
		req.setStatus("0");
		return adMaterialService.getList(req);
	}

	/**
	 * 个人收藏列表
	 *
	 * @param req
	 * @return
	 */
	@SysLog("个人收藏列表")
	@RequestMapping("/getCollectList")
	public R getCollectByList(@RequestBody AdMaterialVo req) {
		// 设置查询收藏列表
		req.setIsCollect("true");
		req.setStatus("0");
		return adMaterialService.getList(req);
	}

	/**
	 * 个人收藏列表
	 *
	 * @param req
	 * @return
	 */
	@SysLog("个人收藏列表")
	@RequestMapping("/getCollectPage")
	public R getCollectByPage(@RequestBody AdMaterialPageVo req) {
		// 设置查询收藏列表
		req.setIsCollect("true");
		req.setStatus("0");
		return adMaterialService.getPage(req);
	}

	/**
	 * 添加素材
	 *
	 * @param req
	 * @return
	 */
	@SysLog("添加素材")
	@RequestMapping("/addMaterial")
	public R addMaterial(AdMaterialVo req) {
		// 验证数据
		R result = checkParam(req);
		if (0 != result.getCode()) {
			return result;
		}
		return adMaterialService.addMaterial(req);
	}

	/**
	 * 添加素材
	 * 炎帝推送素材使用
	 *
	 * @param adMaterial
	 * @return
	 */
	@Inner
	@SysLog("添加炎帝素材")
	@RequestMapping("/addYanDiMaterial")
	public R addYanDiMaterial( AdMaterialVo adMaterial) {
		// 验证数据
		R result = checkParam(adMaterial);
		if (0 != result.getCode()) {
			return result;
		}
		return adMaterialService.addMaterial(adMaterial);
	}

	/**
	 * 编辑
	 *
	 * @param req
	 * @return
	 */
	@SysLog("编辑素材")
	@RequestMapping("/edit")
	public R edit(AdMaterialVo req) {
		if (StringUtils.isBlank(req.getId())) {
			return R.failed("未获取到素材ID");
		}

		// 修改名称验证
		if (StringUtils.isNotBlank(req.getNames())) {
			QueryWrapper<AdMaterial> wrapper = new QueryWrapper<>();
			wrapper.eq("name", req.getNames());
			wrapper.eq("is_delete", 0);
			wrapper.notIn("id", req.getId());
			wrapper.last("LIMIT 1");
			// 素材名称不能重复
			AdMaterial adMaterial = adMaterialService.getOne(wrapper);
			if (Objects.nonNull(adMaterial)) {
				return R.failed("素材名称：" + adMaterial.getName() + "，不能重复");
			}
		}

		// 验证数据
		R result = this.checkParam(req);
		if (0 != result.getCode()) {
			return result;
		}
		return adMaterialService.edit(req);
	}

	/**
	 * 删除
	 *
	 * @param req
	 * @return
	 */
	@SysLog("删除素材")
	@RequestMapping("/del")
	public R del( AdMaterialVo req) {
		if (StringUtils.isBlank(req.getIds())) {
			return R.failed("未获取到素材ID");
		}
		return adMaterialService.del(req);
	}

	/**
	 * 根据id查询素材信息
	 *
	 * @param req
	 * @return
	 */
	@RequestMapping("/findById")
	public R findById(@RequestBody AdMaterialVo req) {
		if (StringUtils.isBlank(req.getPlatformFileId())) {
			return R.failed("未获取到三方平台文件ID");
		}
		return adMaterialService.findById(req);
	}

	/**
	 * 添加收藏
	 *
	 * @param req
	 * @return
	 */
	@SysLog("添加收藏")
	@RequestMapping("/addCollect")
	public R addCollect(@RequestBody AdMaterialVo req) {
		if (StringUtils.isBlank(req.getId())) {
			return R.failed("未获取到素材ID");
		}
		return adMaterialService.addCollect(req);
	}

	/**
	 * 删除收藏
	 *
	 * @param req
	 * @return
	 */
	@SysLog("删除收藏")
	@RequestMapping("/delCollect")
	public R delCollect(@RequestBody AdMaterialVo req) {
		if (StringUtils.isBlank(req.getIds())) {
			return R.failed("未获取到素材ID");
		}
		return adMaterialService.delCollect(req);
	}

	/**
	 * 批量推送
	 *
	 * @param req
	 * @return
	 */
	@SysLog("同步素材")
	@RequestMapping("/pushMaterial")
	public R pushMaterial(@RequestBody AdMaterialVo req){
		if (StringUtils.isBlank(req.getIds())) {
			return R.failed(null, "未获取到素材ID");
		}
		if (StringUtils.isBlank(req.getPlatformId())) {
			return R.failed(null, "未获取到平台类型");
		}
		if (StringUtils.isBlank(req.getAdvertiserIds())) {
			return R.failed(null, "未获取到广告账户");
		}
		R r;
		try{
			r = adMaterialService.pushMaterial(req);
		}catch (Exception e){
			return R.failed(null, e.getMessage());
		}
		return r;
	}


	/**
	 * 参数验证
	 *
	 * @param req
	 * @return
	 */
	public R checkParam(AdMaterialVo req) {
		Integer tagLimit = sdkProperties.getTagLimit();
		if(StringUtils.isNotBlank(req.getTagIds()) && req.getTagIds().split(",").length > tagLimit){
			return R.failed("最多选择"+tagLimit+"个标签");
		}
		String[] nameArr = req.getNames().split(",");
		if(CollectionUtils.isEmpty(req.getFiles())){
			String name = nameArr[0];
			if (name.indexOf(Constants.POINT) > 0) {
				name = name.substring(0, name.lastIndexOf(Constants.POINT));
			}
			if (StringUtils.isBlank(name) || name.length() > 15) {
				return R.failed("名称过长(名称限制最多15个字符)");
			}
		}
		if (StringUtils.isBlank(req.getId())
				// 修改素材，素材不为空，验证素材
				|| (StringUtils.isNotBlank(req.getId()) && null != req.getFiles())) {
			Collection<MultipartFile> files = req.getFiles();
			if (null == files) {
				return R.failed("未获取到素材文件");
			}
			if (StringUtils.isBlank(req.getNames())) {
				return R.failed("未获取到视频名称");
			}
			// 视频已被推送至第三方平台无法修改
			if (StringUtils.isNotBlank(req.getId()) && null != files){
				List<AdMaterialPlatform> adMaterialPlatformList = adMaterialPlatformService.list(Wrappers.<AdMaterialPlatform>lambdaQuery()
						.eq(AdMaterialPlatform::getMaterialId,req.getId()));
				if (CollectionUtils.isNotEmpty(adMaterialPlatformList)){
					return R.failed("原视频已被推送至第三方平台无法修改");
				}
			}
			List<String> nameList = Lists.newArrayList();
			for (MultipartFile file : req.getFiles()) {
				// 获取传入的名称
				String name = nameArr[nameList.size()];
				// 获取文件名称
				String fileName = file.getOriginalFilename();
				// 获取文件后缀名
				String suffix = fileName.substring(fileName.lastIndexOf(Constants.POINT) + 1).toLowerCase();

				if (name.indexOf(Constants.POINT) > 0) {
					name = name.substring(0, name.lastIndexOf(Constants.POINT));
				}

				if (StringUtils.isBlank(name) || name.length() > 15) {
					return R.failed("名称过长(名称限制最多15个字符)");
				}
				if (name.indexOf("_") >= 0) {
					return R.failed("素材名称不能包含特殊字符下划线");
				}
				if (name.indexOf("@") >= 0) {
					return R.failed("素材名称不能包含特殊字符@");
				}
				if (nameList.contains(name)) {
					return R.failed("素材名称：" + name + "，不能重复");
				}
				QueryWrapper<AdMaterial> wrapper = new QueryWrapper<>();
				wrapper.eq("name", name + Constants.POINT + suffix);
				wrapper.eq("is_delete", 0);
				if (StringUtils.isNotBlank(req.getId())) {
					wrapper.notIn("id", req.getId());
				}
				wrapper.last("LIMIT 1");
				// 素材名称不能重复
				AdMaterial adMaterial = adMaterialService.getOne(wrapper);
				if (Objects.nonNull(adMaterial)) {
					return R.failed("素材名称：" + adMaterial.getName() + "，不能重复");
				}
				nameList.add(name);
			}

			List<String> md5List = Lists.newArrayList();
			for (MultipartFile file : req.getFiles()) {
				try {
					// 查看素材是否已经上传
					String md5 = DigestUtils.md5Hex(file.getInputStream());
					if (md5List.contains(md5)) {
						return R.failed("素材不能重复上传");
					}
					QueryWrapper<AdMaterial> wrapper = new QueryWrapper<>();
					wrapper.eq("md5", md5);
					wrapper.eq("is_delete", 0);
					// 判断是否从个人素材上传素材
					if ("true".equals(req.getIsCollect())) {
						wrapper.eq("readpop", 1);
						wrapper.eq("create_user", SecurityUtils.getUser().getId());
					} else {
						wrapper.eq("readpop", 0);
					}
					wrapper.last("LIMIT 1");
					AdMaterial adMaterial1 = adMaterialService.getOne(wrapper);
					if (Objects.nonNull(adMaterial1)) {
						return R.failed(adMaterial1, "此素材已存在，ID：" + adMaterial1.getId() + "，名称：" + adMaterial1.getName());
					}
					md5List.add(md5);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		if (StringUtils.isBlank(req.getMainGameId())) {
			return R.failed("未获取到主游戏");
		}
		if (StringUtils.isBlank(req.getGameId())) {
			return R.failed("未获取到子游戏");
		}
		if (StringUtils.isBlank(req.getCreatorId())) {
			return R.failed("未获取到创意者");
		}
		if (StringUtils.isBlank(req.getMakerId())) {
			return R.failed("未获取到制作者");
		}
		if (StringUtils.isBlank(req.getMakeType())) {
			return R.failed("未获取到制作类型");
		}
		return R.ok();
	}

	/**
	 * 根据素材ID列表查询素材信息
	 *
	 * @param materialIds
	 * @return
	 */
	@Inner
	@PostMapping("/getMaterialListByIds")
	public R<List<AdMaterial>> getMaterialListByIds(@RequestBody List<Long> materialIds) {
		final List<AdMaterial> materialList = adMaterialService.getMaterialListByIds(materialIds);
		return R.ok(materialList);
	}


	/**
	 * 素材数据报表
	 *
	 * @return
	 */
	@SysLog("素材数据报表")
	@PostMapping("/getMaterialReportList")
	public R<List<AdMaterialReportDO>> getMaterialReportList(@RequestBody AdMaterialReportVo amr) {
		if (StringUtils.isEmpty(amr.getSdate()) || StringUtils.isEmpty(amr.getEdate())) {
			return R.failed("时间范围不能为空");
		}
		List<AdMaterialReportDO> materialReportList = adMaterialService.getMaterialReportList(amr);
		return R.ok(materialReportList);
	}

	/**
	 * 批量下载视频
	 * @param request
	 * @param response
	 * @param ids
	 */
	@SneakyThrows
	@GetMapping(value = "downLoad")
	public void downLoad(HttpServletRequest request, HttpServletResponse response, @RequestParam String ids) {

		if (StringUtils.isBlank(ids)) {
			return;
		}

		List<Long> materialIds = new ArrayList<>(Arrays.asList(ids.split(",")))
				.stream().filter(i -> StringUtils.isNotBlank(i)).map(v -> Long.valueOf(v)).distinct().collect(Collectors.toList());

		List<AdMaterial> materialList = adMaterialService.list(Wrappers.<AdMaterial>lambdaQuery()
				.eq(AdMaterial::getIsDelete,0)
				.in(AdMaterial::getId,materialIds));
		if (CollectionUtils.isEmpty(materialList)) {
			return;
		}

		// 生成压缩文件可以参考hutool工具类
		// public static File zip(File zipFile, String[] paths, InputStream[] ins) -- ZipUtil.zip

		response.setCharacterEncoding("UTF-8");
		response.setHeader("Content-Type", "application/octet-stream");

		//单个文件不压缩
		if (materialList.size() == 1) {
			AdMaterial material = materialList.get(0);
			String fileName = URLEncoder.encode(material.getName(), "UTF-8").replaceAll("\\+", "%20");
			//解决文件名乱码，URLEncoder.encode(fileName, "UTF8") 不能解决特殊字符的问题
//			fileName = new String(fileName.getBytes("gbk"), "iso8859-1");
			response.addHeader("Content-Disposition", "attachment;filename=" + fileName);
			//单个文件输出
			@Cleanup OutputStream ops = new BufferedOutputStream(response.getOutputStream());
			URL url = new URL(material.getFileUrl());
			@Cleanup InputStream is = url.openStream();
			this.write(ops,is);
		} else {
			//获取压缩文件名称集合、颜色URL集合;关系一一对应
			Map<String,String> nameUrlMap = materialList.stream()
					.collect(Collectors.toMap(AdMaterial::getName,AdMaterial::getFileUrl,(o,n)->o));
			response.addHeader("Content-Disposition", "attachment;filename=" + "Material.zip");
			@Cleanup ZipOutputStream zipOps = new ZipOutputStream(new BufferedOutputStream(response.getOutputStream()), Charset.forName("utf-8"));
			zipOps.setMethod(ZipOutputStream.DEFLATED);
			for (String key: nameUrlMap.keySet()) {
				URL url = new URL(nameUrlMap.get(key));
				@Cleanup InputStream is = url.openStream();
				zipOps.putNextEntry(new ZipEntry(key));
				this.write(zipOps,is);
				is.close();
				//关闭当前zip
				zipOps.closeEntry();
			}
		}
	}

	private void write(OutputStream ops,InputStream is) throws IOException {
		byte[] buff = new byte[1024*10];
		int len = 0;
		while ((len = is.read(buff)) != -1) {
			ops.write(buff,0,len);
		}
	}

}
